/*******************************************************************************
 * Copyright (c) 2009 Kevin Bierhoff and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Kevin Bierhoff - initial API and implementation
 *******************************************************************************/
package com.googlecode.twelfdevtools.editor;

import java.util.LinkedList;
import java.util.List;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;

import com.googlecode.twelfdevtools.model.ITwelfNamedElement;
import com.googlecode.twelfdevtools.twelfast.AbstractTwelfAstVisitor;
import com.googlecode.twelfdevtools.twelfast.Application;
import com.googlecode.twelfdevtools.twelfast.Arrow;
import com.googlecode.twelfdevtools.twelfast.Ascription;
import com.googlecode.twelfdevtools.twelfast.ConstantDeclaration;
import com.googlecode.twelfdevtools.twelfast.ITwelfIdentifier;
import com.googlecode.twelfdevtools.twelfast.Identifier;
import com.googlecode.twelfdevtools.twelfast.Lambda;
import com.googlecode.twelfdevtools.twelfast.Pi;
import com.googlecode.twelfdevtools.twelfast.Type;
import com.googlecode.twelfdevtools.twelfast.Wildcard;

/**
 * @author kevin
 * @since Nov 15, 2009
 */
public class OccurrencesFinder extends AbstractTwelfAstVisitor<List<OccurrencesFinder.Occurrence>> {
	
	public final class Occurrence {
		public final ITwelfIdentifier id;
		public boolean isDefinition;
		public int offset = -1;
		public int length = 0;

		public Occurrence(ITwelfIdentifier id) {
			this.id = id;
		}
	}

	private final List<Occurrence> result = new LinkedList<Occurrence>();
	private final ITwelfNamedElement element;
	private final IDocument doc;
	private IRegion elementRange;

	public OccurrencesFinder(ITwelfNamedElement element, IDocument doc) {
		this.element = element;
		this.doc = doc;
		try {
			this.elementRange = element.getIdentifierRange(doc);
		} catch (BadLocationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public List<Occurrence> getResult() {
		return result;
	}

	@Override
	public List<Occurrence> app(Application node) {
		doDispatch(node.getLhs());
		doDispatch(node.getRhs());
		return result;
	}

	@Override
	public List<Occurrence> arrow(Arrow node) {
		doDispatch(node.getLhs());
		doDispatch(node.getRhs());
		return result;
	}

	@Override
	public List<Occurrence> ascr(Ascription node) {
		doDispatch(node.getTerm());
		doDispatch(node.getAscription());
		return result;
	}

	@Override
	public List<Occurrence> constDecl(ConstantDeclaration node) {
		check(node.getConst(), true);
		doDispatch(node.getValue());
		doDispatch(node.getTerm());
		return result;
	}

	@Override
	public List<Occurrence> id(Identifier node) {
		check(node, false);
		return result;
	}

	@Override
	public List<Occurrence> lambda(Lambda node) {
		check(node.getId(), true);
		doDispatch(node.getIdTerm());
		doDispatch(node.getTerm());
		return result;
	}

	@Override
	public List<Occurrence> pi(Pi node) {
		check(node.getId(), true);
		doDispatch(node.getIdTerm());
		doDispatch(node.getTerm());
		return result;
	}

	@Override
	public List<Occurrence> type(Type node) {
		return result;
	}

	@Override
	public List<Occurrence> wildcard(Wildcard node) {
		return result;
	}
	
	private void check(Identifier node, boolean def) {
		// TODO compare bindings instead of just names....
		if (node.getId().equals(element.getId())) {
//		ITwelfBinding typeBinding = node.getBinding();
//		if (typeBinding != null && element.equals(typeBinding.getModelElement())) {
			Occurrence occ = new Occurrence(node);
			try {
				occ.offset = doc.getLineOffset(node.getStartRow()-1) + node.getStartColumn()-1;
				int end = doc.getLineOffset(node.getEndRow()-1) + node.getEndColumn()-1;
				occ.length = end - occ.offset;
				def = def || (elementRange != null && 
					elementRange.getOffset() ==	occ.offset &&
					elementRange.getLength() == occ.length);
			} catch (BadLocationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			occ.isDefinition = def;
			
			// TODO implicitly defined variables
			result.add(occ);
		}
	}
}
